//
//  YNXMPPManager.m
//  YNIMFramework
//
//  Created by 黄旭 on 2021/7/16.
//

#import "YNXMPPManager.h"
#import "YNSLRoster.h"
#import "YNIMTool.h"
#import "XMPPFramework.h"
#import "YNIMFrameworkMacro.h"
#import "YNMessagerReceiveHandle.h"
#import "YNGroupAndDiscussion.h"

@interface YNXMPPManager()<XMPPStreamDelegate ,XMPPReconnectDelegate, XMPPAutoPingDelegate, UIAlertViewDelegate>
{
    XMPPStream                          *xmppStream;
    XMPPReconnect                       *xmppReconnect;
    XMPPAutoPing                        *xmppAutoPing;
    //用户名册相关处理类
    YNSLRoster                          *ynslRoster;
    //消息收发相关处理类
    YNMessagerReceiveHandle             *messageHandle;
    //群组相关处理类
    YNGroupAndDiscussion                *groupHandle;
}
@property(nonatomic,copy)NSString*companycode;
@property(nonatomic,copy)NSString*server;
@property(nonatomic,copy)NSString*userId;
@property(nonatomic,copy)NSString*userName;
@property(nonatomic,assign)BOOL mini;
@property(nonatomic,copy)NSString*domain;
@property(nonatomic,copy)NSString*resource;
@property(nonatomic,weak)id<YNXMPPprotocol> delegate;
//登录操作是否正在进行中
@property(nonatomic,assign)BOOL newLogining;
@end

@implementation YNXMPPManager

- (void)dealloc
{
    [self teardownStream];
}

-(instancetype)initXMPPWithServer:(NSString*)server companyCode:(NSString*)code imId:(NSString*)imId loginName:(NSString*)loginName delegate:(id<YNXMPPprotocol>)delegate minimalist:(BOOL)mini{
    if (self = [super init]) {
        self.companycode = code;
        self.userId = imId;
        self.userName = loginName;
        self.delegate = delegate;
        self.server = server;
        self.mini = mini;
        [self initConnectWithServer:server companyCode:code imId:imId delegate:delegate minimalist:mini];
    }
    return self;
}

-(void)initConnectWithServer:(NSString*)server companyCode:(NSString*)code imId:(NSString*)imId delegate:(id<YNXMPPprotocol>)delegate minimalist:(BOOL)mini{
    xmppStream = [[XMPPStream alloc] init];
    //允许后台模式(注意ios模拟器上是不支持后台socket的)
#if !TARGET_IPHONE_SIMULATOR
    {
        xmppStream.enableBackgroundingOnSocket = YES;
    }
#endif
    xmppReconnect = [[XMPPReconnect alloc] init];
    xmppReconnect.autoReconnect = YES;
    
    xmppAutoPing = [[XMPPAutoPing alloc] init];
    xmppAutoPing.respondsToQueries = YES;
    xmppAutoPing.pingInterval = 60;
    xmppAutoPing.pingTimeout = 10;
    xmppAutoPing.targetJID = nil;
    [xmppReconnect activate:xmppStream];
    [xmppAutoPing activate:xmppStream];
    
    [xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
    [xmppReconnect addDelegate:self delegateQueue:dispatch_get_main_queue()];
    [xmppAutoPing addDelegate:self delegateQueue:dispatch_get_main_queue()];
    
    NSArray *array = [server componentsSeparatedByString:@":"];
    if (array.count  == 2)
    {
        debugLog(@"HostName=%@,HostPort=%@,2",[array firstObject],[array lastObject]);
        
        [xmppStream setHostName:[array firstObject]];
        
        [xmppStream setHostPort:[[array lastObject] intValue]];
    }else{
        debugLog(@"请输入正确的服务器地址，形似：10.2.3.65:6000");
        return;
    }
    if (!mini) {
        //初始化用户名册、消息收发管理类
        ynslRoster = [[YNSLRoster alloc] initWithCode:code userId:imId delegate:delegate];
        ynslRoster.autoFetchRoster = YES;
        [ynslRoster activate:xmppStream];
        //初始化消息收发管理类
        messageHandle = [[YNMessagerReceiveHandle alloc]initWith:xmppStream delegate:delegate];
        //群组相关处理类
        groupHandle = [[YNGroupAndDiscussion alloc]initWithDelegate:delegate];
        [groupHandle activate:xmppStream];
    }
}

///重新启动开始xmpp
-(void)againXmpp{
    [self teardownStream];
    [self initConnectWithServer:self.server companyCode:self.companycode imId:self.userId delegate:self.delegate minimalist:self.mini];
    [self connectWithDomain:self.domain resource:self.resource];
}
///刷新xmpp连接，如果断开则执行重新登录
-(void)refreshXmppConnect{
    XMPPConnectType xmppConnectType = [self getXMPPConnectStatus];
    
    switch (xmppConnectType)
    {
        case XMPPConnectTypeBreak:
        {
            //已经断开连接，并且当前没有正在进行的登录操作，需要进行连接
            if (self.newLogining == NO) {
                [self againXmpp];
            }
        }
            break;
            
        case XMPPConnectTypeConnecting:
        {
            //正在连接，不进行操作，等待连接成功后结束下拉状态
        }
            break;
            
        default:
        {
            //openfire已经连接完成，但是网络请求是否成功则不一定，在请求通知和系统消息及视频会议消息接口响应后设置下拉状态
        }
            break;
    }
}

#pragma mark - Connect/disConnect
//return 错误信息描述，为空则为正确
- (NSString*)connectWithDomain:(NSString*)domain resource:(NSString*)resource
{
    if ([xmppStream isConnected])
    {
        return nil;
    }
    if (self.userId == nil)
    {
        return @"用户错误";
    }
    self.newLogining = YES;
    self.domain = domain;
    self.resource = resource;
    NSString *jidStr = [NSString stringWithFormat:@"%@@%@",self.userId,domain];
    XMPPJID *myJID = [XMPPJID jidWithString:jidStr resource:resource];
    [xmppStream setMyJID:myJID];
    
    NSError *error;
    if (![xmppStream connectWithTimeout:XMPPStreamTimeoutNone error:&error])
    {
        debugLog(@"与openfire建立通讯连接错误 Error connecting: %@", error);
        self.newLogining = NO;
        return @"建立通讯连接错误";
    }
    return nil;
}
//认证
-(void)xmppAuthenticatePassword:(NSString*)password{
    NSError *error;
    if (![xmppStream authenticate:[[XMPPPlainAuthentication alloc] initWithStream:xmppStream password:password] error:&error]) {
        debugLog(@"openfire密码验证失败:%@",error);
        if ([self.delegate respondsToSelector:@selector(didError:)]) {
            [self.delegate didError:@"身份验证失败"];
        }
    }
}
//获取初始化信息
-(void)getLoginUserInfor{
    if (ynslRoster != nil && [ynslRoster autoFetchRoster])
    {
        [ynslRoster searchMeInfomationWithNo:self.userId];
    }
}
//获取初始化信息成功后，获取组织机构，群，讨论组等等信息.
- (void)fetchRosterData{
    [ynslRoster fetchRosterData];
}
//上线
- (void)goOnline
{
    XMPPPresence *presence = [XMPPPresence presence];
    NSXMLElement *status = [NSXMLElement elementWithName:@"status" stringValue:@"online"];
    [presence addChild:status];
    [xmppStream sendElement:presence];
}

//离线
- (void)goOffline
{
    XMPPPresence *presence = [XMPPPresence presenceWithType:@"unavailable"];
    [xmppStream sendElement:presence];
}

- (void)disConnect
{
    [self goOffline];
    [xmppStream disconnect];
}

-(XMPPStream*)getXMPPStream{
    return xmppStream;
}

-(YNSLRoster*)getRoster{
    return ynslRoster;
}

-(YNGroupAndDiscussion*)getGroupHandle{
    return groupHandle;
}

-(YNMessagerReceiveHandle*)getMessageHandle{
    return messageHandle;
}

#pragma mark - XMPPStrem Delegate

- (void)xmppStream:(XMPPStream *)sender socketDidConnect:(GCDAsyncSocket *)socket
{
    debugLog(@"openfireSocket连接成功");
}

//安全相关
- (void)xmppStream:(XMPPStream *)sender willSecureWithSettings:(NSMutableDictionary *)settings
{
    NSString *expectedCertName = nil;
    NSString *serverDomain = xmppStream.hostName;
    NSString *virtualDomain = [xmppStream.myJID domain];
    
    if (serverDomain == nil)
    {
        expectedCertName = virtualDomain;
    }
    else
    {
        expectedCertName = serverDomain;
    }
    
    if (expectedCertName)
    {
        [settings setObject:expectedCertName forKey:(NSString *)kCFStreamSSLPeerName];
    }
}

//连接服务器成功进行认证
- (void)xmppStreamDidConnect:(XMPPStream *)sender
{
    debugLog(@"xmppStrean连接成功,开始进行认证");
    dispatch_async(dispatch_get_main_queue(), ^{
        if ([self.delegate respondsToSelector:@selector(xmppStreamDidConnect)]) {
            [self.delegate xmppStreamDidConnect];
        }
    });
}

//认证成功(可以做一些处理)
- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender
{
    debugLog(@"openfire认证成功");
    dispatch_async(dispatch_get_main_queue(), ^{
        if ([self.delegate respondsToSelector:@selector(xmppStreamDidAuthenticate)]) {
            [self.delegate xmppStreamDidAuthenticate];
        }
        self.newLogining = NO;
    });
}

- (void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(DDXMLElement *)error
{
    debugLog(@"openfire认证错误:%@", error);
    dispatch_async(dispatch_get_main_queue(), ^{
        if ([self.delegate respondsToSelector:@selector(didNotAuthenticate)]) {
            [self.delegate didNotAuthenticate];
        }
        self.newLogining = NO;
    });
}

- (void)xmppStreamConnectDidTimeout:(XMPPStream *)sender
{
    debugLog(@"连接超时");
    dispatch_async(dispatch_get_main_queue(), ^{
        if ([self.delegate respondsToSelector:@selector(xmppStreamConnectDidTimeout)]) {
            [self.delegate xmppStreamConnectDidTimeout];
        }
        self.newLogining = NO;
    });
}

//同时登录处理
- (void)xmppStream:(XMPPStream *)sender didReceiveError:(DDXMLElement *)error
{
    debugLog(@"xmppStream收到错误信息：%@",error);
    //如果收到的错误是被挤掉线
    for (DDXMLNode *node in [error children])
    {
        if ([[node name] isEqualToString:@"conflict"])
        {
            dispatch_async(dispatch_get_main_queue(), ^{
                if ([self.delegate respondsToSelector:@selector(sameTimeLogin)]) {
                    [self.delegate sameTimeLogin];
                }
            });
            return;
        }
    }
    if ([self.delegate respondsToSelector:@selector(didError:)]) {
        [self.delegate didError:@"通讯错误"];
    }
}

//已经断开连接
- (void)xmppStreamDidDisconnect:(XMPPStream *)sender withError:(NSError *)error
{
    debugLog(@"xmppStream断开连接，错误信息: %@",error);
    if (![xmppStream isConnected])
    {
        debugLog(@"Unable to connect to server. Check xmppStream.hostName");
    }
    switch ([error code])
    {
        case 7:
        {
            debugLog(@"远程服务器关闭");
        }
            break;
        case 57:
        {
            debugLog(@"已经认证成功后，当前网络断开");
        }
            break;
        case 51:
        {
            debugLog(@"登录，没有网络");
        }
            break;
        case 61:
            debugLog(@"远程服务器异常");
            break;
        case 8:
            debugLog(@"服务器域名错误");
            break;
        case 64:
            debugLog(@"主机已关闭");
            break;
        case 2:
            debugLog(@"帐号错误，JID有问题");
            break;
        case 60:
        {
            debugLog(@"自动重新连接的时候操作超时");
        }
            break;
        case 49:
            debugLog(@"无效的地址");
            break;
        case 4:
            break;
        default:
            break;
    }
    dispatch_async(dispatch_get_main_queue(), ^{
        if ([self.delegate respondsToSelector:@selector(xmppStreamDidDisconnect:)]) {
            [self.delegate xmppStreamDidDisconnect:[error code]];
        }
        self.newLogining = NO;
    });
}
#pragma mark - XMPPReconnectDelegate
- (void)xmppReconnect:(XMPPReconnect *)sender didDetectAccidentalDisconnect:(SCNetworkConnectionFlags)connectionFlags
{
}

- (BOOL)xmppReconnect:(XMPPReconnect *)sender shouldAttemptAutoReconnect:(SCNetworkConnectionFlags)connectionFlags
{
    if (connectionFlags)
    {
        return YES;
    }
    else
    {
        return NO;
    }
}

#pragma mark - XMPPAutoPingDelegate
- (void)xmppAutoPingDidReceivePong:(XMPPAutoPing *)sender
{
    debugLog(@"%@: %@ - %@",THIS_FILE, THIS_METHOD, sender);
}

- (void)xmppAutoPingDidSendPing:(XMPPAutoPing *)sender
{
    debugLog(@"%@: %@ - %@",THIS_FILE, THIS_METHOD, sender);
}

- (void)xmppAutoPingDidTimeout:(XMPPAutoPing *)sender
{
    debugLog(@"%@: %@ - %@",THIS_FILE, THIS_METHOD, sender);
}

//退出登录
- (void)logout
{
    [self teardownStream];
}

///判断是否需要重新登录
- (BOOL)isNeedLoginAgain
{
    if (xmppStream)
    {
        if ([xmppStream isConnected] && [xmppStream isAuthenticated])
        {
            return NO;
        }
    }
    
    return YES;
}

//获取Steam连接状态
- (XMPPConnectType)getXMPPConnectStatus
{
    XMPPConnectType connectType;
    
    if (xmppStream)
    {
        if ([xmppStream isConnected] && [xmppStream isAuthenticated])
        {
            debugLog(@"已经连接");
            connectType = XMPPConnectTypeFinish;
        }
        else if (([xmppStream isConnecting] && [xmppStream isAuthenticated])||[xmppStream isAuthenticating])
        {
            debugLog(@"正在连接");
            connectType = XMPPConnectTypeConnecting;
        }
        else if ([xmppStream isAuthenticating])
        {
            debugLog(@"正在连接");
            connectType = XMPPConnectTypeConnecting;
        }
        else
        {  //断开Stream
            connectType = XMPPConnectTypeBreak;
        }
    }
    else
    {
        connectType = XMPPConnectTypeBreak;
    }
    return connectType;
}

- (void)teardownStream
{
    [xmppStream removeDelegate:self];
    [xmppReconnect removeDelegate:self];
    [xmppAutoPing removeDelegate:self];
    [messageHandle removeDelegate];
    [ynslRoster deactivate];
    [xmppReconnect deactivate];
    [xmppAutoPing deactivate];
    [groupHandle deactivate];
    [self disConnect];
    ynslRoster = nil;
    xmppStream = nil;
    xmppReconnect = nil;
    xmppAutoPing = nil;
    messageHandle = nil;
    groupHandle = nil;
}

@end
